﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;

namespace MAF.Common
{
    /// <summary>
    /// 文件存储策略
    /// </summary>
    public class FileStorageStrategy
    {
        private string RegInvalidFileNameChars = @"[/\\:*?""<>|\t]";

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="path"></param>
        public FileStorageStrategy(string path)
        {
            if ( null == path || string.Empty.Equals(path=path.Trim()) )
                throw new ArgumentNullException("根目录路径不能为空");

            _Root = path;
        }

        /// <summary>
        /// 文件存储的根目录
        /// </summary>
        private string _Root = string.Empty;
        public string Root 
        {
            get { return _Root; }
        }

        /// <summary>
        /// 把文件保存到根目录的某个子目录下,
        /// 这里是个模板方法，可以通过重写NewDirName,NewFileName属性来改变存储的方式
        /// </summary>
        /// <param name="stream">Stream对象</param>
        /// <param name="format">文件后缀</param>
        /// <returns>
        /// 长度为2的数组
        ///     0 是目录名
        ///     1 是文件名
        /// </returns>
        public string[] Save(Stream stream,string format)
        {
            if (null==stream)
                throw new ArgumentException("保存失败，参数为null");
            if (null == format)
                format = string.Empty;

            string[] dirAndFilename = this.DirAndFileName;
            string filePath = this.GetFilePath(ref dirAndFilename,format);
            Directory.CreateDirectory(Path.GetDirectoryName(filePath));

            try
            {
                //读取流
                byte[] buffer = new byte[stream.Length];
                int len = stream.Read(buffer, 0, buffer.Length);

                using (FileStream fstr = new FileStream(filePath, FileMode.Create, FileAccess.Write))
                {
                    fstr.Write(buffer, 0, len);
                    fstr.Close();
                    fstr.Dispose();
                }

                // 更新配置
                this.UpdateConfig(dirAndFilename, UpdateType.Save);
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                stream.Close();
                stream.Dispose();
            }
            return dirAndFilename;
        }

        public void Delete(string[] dirAndFilename)
        {
            File.Delete(this.GetFilePath(ref dirAndFilename));

            // 更新配置
            this.UpdateConfig(dirAndFilename, UpdateType.Delete);
        }

        /// <summary>
        /// 返回文件路径，经过此函数后，后缀名将会被加到第一个参数的文件名中
        /// </summary>
        /// <param name="dirAndfilename"></param>
        /// <returns></returns>
        public string GetFilePath(ref string[] dirAndfilename,string format)
        {
            if (null == dirAndfilename || 2 != dirAndfilename.Length)
                throw new ArgumentNullException();

            string dirName = dirAndfilename[0];
            string filename = dirAndfilename[1];

            if (string.IsNullOrEmpty(filename))
                throw new ArgumentNullException("文件名不能为空");

            // 文件名清理
            filename = filename.Trim();
            filename = Regex.Replace(filename,RegInvalidFileNameChars,"");
            if( string.Empty == filename )
                throw new ArgumentException("文件名中只有特殊字符，没有正常字符");
            
            // 如果有后缀名
            if (!string.IsNullOrEmpty(format))
            {
                // 文件后缀名清理
                format = format.Trim(" .".ToCharArray());
                format = Regex.Replace(format, RegInvalidFileNameChars, "");
                if (string.Empty != format)
                    filename = string.Format(@"{0}.{1}", filename, format);
            }

            // 目录名清理
            string filePath = filename;
            if (!string.IsNullOrEmpty(dirName))
            {
                dirName = dirName.Trim();
                dirName = Regex.Replace(dirName, RegInvalidFileNameChars, "");
                if (string.Empty != dirName)
                    filePath = string.Format(@"{0}\{1}", dirName, filePath);
            }
            
            // ref
            dirAndfilename[0] = dirName;
            dirAndfilename[1] = filename;

            return Path.Combine(this.Root, filePath);
        }
        public string GetFilePath(ref string[] dirAndfilename)
        {
            return GetFilePath(ref dirAndfilename,null);
        }

        #region 策略实现者需要重写此两个方法

        /// <summary>
        /// 新建文件名，此方法是非常简陋的，请根据需要重写文件名策略
        /// 返回值第一个是目录名，第二个才是文件名
        /// </summary>
        /// <returns></returns>
        protected virtual string[] DirAndFileName
        {
            get { return new string[]{string.Empty,DateTime.Now.ToString("yyyyMMddhhmmss")}; }
        }

        /// <summary>
        /// 可能有些策略需要配置文件，在保存与删除文件后会执行此方法
        /// </summary>
        protected virtual void UpdateConfig(string[] dirAndFilename,UpdateType type) { }

        #endregion

        protected enum UpdateType { Save,Delete}
    }
}
